import {Layout} from '../../src/Layout';
export default Layout;

import docs from 'docs:react-aria-components';
import {ComboBox as VanillaComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import vanillaDocs from 'docs:vanilla-starter/ComboBox';
import '../../tailwind/tailwind.css';
import Anatomy from 'react-aria-components/docs/ComboBoxAnatomy.svg';
import {InlineAlert, Heading, Content} from '@react-spectrum/s2'

export const tags = ['autocomplete', 'search', 'typeahead', 'input'];
export const relatedPages = [
  {title: 'useComboBox', url: 'ComboBox/useComboBox.html'},
  {title: 'Testing ComboBox', url: './ComboBox/testing'}
];
export const description = 'Combines a text input with a listbox, allowing users to filter a list of options to items matching a query.';

# ComboBox

<PageDescription>{docs.exports.ComboBox.description}</PageDescription>

<ExampleSwitcher>
  ```tsx render docs={vanillaDocs.exports.ComboBox} links={vanillaDocs.links} props={['label', 'isDisabled']} initialProps={{label: 'Favorite Animal', placeholder: 'Select a flavor'}} type="vanilla" files={["starters/docs/src/ComboBox.tsx", "starters/docs/src/ComboBox.css"]}
  "use client";
  import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';

  <ComboBox/* PROPS */>
    <ComboBoxItem>Aardvark</ComboBoxItem>
    <ComboBoxItem>Cat</ComboBoxItem>
    <ComboBoxItem>Dog</ComboBoxItem>
    <ComboBoxItem>Kangaroo</ComboBoxItem>
    <ComboBoxItem>Panda</ComboBoxItem>
    <ComboBoxItem>Snake</ComboBoxItem>
  </ComboBox>
  ```

  ```tsx render docs={vanillaDocs.exports.ComboBox} links={vanillaDocs.links} props={['label', 'isDisabled']} initialProps={{label: 'Favorite Animal', placeholder: 'Select a flavor'}} type="tailwind" files={["starters/tailwind/src/ComboBox.tsx"]}
  "use client";
  import {ComboBox, ComboBoxItem} from 'tailwind-starter/ComboBox';

  <ComboBox/* PROPS */>
    <ComboBoxItem>Aardvark</ComboBoxItem>
    <ComboBoxItem>Cat</ComboBoxItem>
    <ComboBoxItem>Dog</ComboBoxItem>
    <ComboBoxItem>Kangaroo</ComboBoxItem>
    <ComboBoxItem>Panda</ComboBoxItem>
    <ComboBoxItem>Snake</ComboBoxItem>
  </ComboBox>
  ```

</ExampleSwitcher>

## Content

`ComboBox` reuses the `ListBox` component, following the [Collection Components API](collections?component=ComboBox). It supports ListBox features such as static and dynamic collections, sections, disabled items, links, text slots, asynchronous loading, etc. See the [ListBox docs](ListBox) for more details.

The following example shows a dynamic collection of items, grouped into sections.

```tsx render
"use client";
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {ListBoxSection, Collection, Header} from 'react-aria-components';

function Example() {
  /*- begin collapse -*/
  let options = [
    {name: 'Fruit', children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Orange'},
      {name: 'Honeydew'},
      {name: 'Grapes'},
      {name: 'Watermelon'},
      {name: 'Cantaloupe'},
      {name: 'Pear'}
    ]},
    {name: 'Vegetable', children: [
      {name: 'Cabbage'},
      {name: 'Broccoli'},
      {name: 'Carrots'},
      {name: 'Lettuce'},
      {name: 'Spinach'},
      {name: 'Bok Choy'},
      {name: 'Cauliflower'},
      {name: 'Potatoes'}
    ]}
  ];
  /*- end collapse -*/

  return (
    /*- begin highlight -*/
    <ComboBox
      label="Preferred fruit or vegetable"
      placeholder="Select an option"
      defaultItems={options}>
      {section => (
        <ListBoxSection id={section.name}>
          <Header>{section.name}</Header>
          <Collection items={section.children}>
            {item => <ComboBoxItem id={item.name}>{item.name}</ComboBoxItem>}
          </Collection>
        </ListBoxSection>
      )}
    </ComboBox>
    /*- end highlight -*/
  );
}
```

## Selection

Use the `defaultSelectedKey` or `selectedKey` prop to set the selected item. The selected key corresponds to the `id` prop of an item. Items can be disabled with the `isDisabled` prop. See the [selection guide](selection?component=ComboBox#single-selection) for more details.

```tsx render
"use client";
import type {Key} from 'react-aria-components';
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {useState} from 'react';

function Example() {
  let [animal, setAnimal] = useState<Key>("bison");

  return (
    <div>
      <ComboBox
        label="Favorite Animal"
        placeholder="Select an animal"
        /*- begin highlight -*/
        selectedKey={animal}
        onSelectionChange={setAnimal}>
        {/*- end highlight -*/}
        <ComboBoxItem id="koala">Koala</ComboBoxItem>
        <ComboBoxItem id="kangaroo">Kangaroo</ComboBoxItem>
        <ComboBoxItem id="platypus" isDisabled>Platypus</ComboBoxItem>
        <ComboBoxItem id="eagle">Bald Eagle</ComboBoxItem>
        <ComboBoxItem id="bison">Bison</ComboBoxItem>
        <ComboBoxItem id="skunk">Skunk</ComboBoxItem>
      </ComboBox>
      <p>Current selection: {animal}</p>
    </div>
  );
}
```

### Input value

Use the `inputValue` or `defaultInputValue` prop to set the value of the input field. By default, the value will be reverted to the selected item on blur. Set the `allowsCustomValue` prop to enable entering values that are not in the list.

```tsx render wide docs={vanillaDocs.exports.ComboBox} links={vanillaDocs.links} props={['allowsCustomValue']} initialProps={{defaultSelectedKey: 'kangaroo'}}
"use client";
import type {Key} from 'react-aria-components';
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {useState} from 'react';

function Example(props) {
  let [value, setValue] = useState<Key>('Kangaroo');

  return (
    <div>
      <ComboBox
        {...props}
        label="Favorite Animal"
        placeholder="Select an animal"
        /*- begin highlight -*/
        /* PROPS */
        inputValue={value}
        onInputChange={setValue}>
        {/*- end highlight -*/}
        <ComboBoxItem id="koala">Koala</ComboBoxItem>
        <ComboBoxItem id="kangaroo">Kangaroo</ComboBoxItem>
        <ComboBoxItem id="platypus">Platypus</ComboBoxItem>
        <ComboBoxItem id="eagle">Bald Eagle</ComboBoxItem>
        <ComboBoxItem id="bison">Bison</ComboBoxItem>
        <ComboBoxItem id="skunk">Skunk</ComboBoxItem>
      </ComboBox>
      <p>Current input value: {value}</p>
    </div>
  );
}
```

### Fully controlled

Both `inputValue` and `selectedKey` can be controlled simultaneously. However, each interaction will only trigger either `onInputChange` or `onSelectionChange`, not both. When controlling both props, you must update both values accordingly.

```tsx render
"use client";
import type {Key} from 'react-aria-components';
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {useState} from 'react';

function ControlledComboBox() {
  /*- begin collapse -*/
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];
  /*- end collapse -*/

  let [fieldState, setFieldState] = useState({
    selectedKey: null,
    inputValue: ''
  });

  let onSelectionChange = (id: Key) => {
    // Update inputValue when selectedKey changes.
    setFieldState({
      inputValue: options.find(o => o.id === id)?.name ?? '',
      selectedKey: id
    });
  };

  let onInputChange = (value: string) => {
    // Reset selectedKey to null if the input is cleared.
    setFieldState(prevState => ({
      inputValue: value,
      selectedKey: value === '' ? null : prevState.selectedKey
    }));
  };

  return (
    <div>
       <ComboBox
        label="Engineering major"
        placeholder="Select a major"
        /*- begin highlight -*/
        defaultItems={options}
        selectedKey={fieldState.selectedKey}
        inputValue={fieldState.inputValue}
        onSelectionChange={onSelectionChange}
        onInputChange={onInputChange}>
        {/*- end highlight -*/}
        {item => <ComboBoxItem>{item.name}</ComboBoxItem>}
      </ComboBox>
      <pre style={{fontSize: 12}}>
        Current selected major id: {fieldState.selectedKey}{'\n'}
        Current input text: {fieldState.inputValue}
      </pre>
    </div>
  );
}
```

## Item actions

Use the `onAction` prop on a `<ListBoxItem>` to perform a custom action when the item is pressed. This example adds a "Create" action for the current input value.

```tsx render
"use client";
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {useState} from 'react';

function Example() {
  let [inputValue, setInputValue] = useState('');

  return (
    <ComboBox
      label="Favorite Animal"
      placeholder="Select an animal"
      allowsEmptyCollection
      inputValue={inputValue}
      onInputChange={setInputValue}>
      {/*- begin highlight -*/}
      {inputValue.length > 0 && (
        <ComboBoxItem onAction={() => alert('Creating ' + inputValue)}>
          {`Create "${inputValue}"`}
        </ComboBoxItem>
      )}
      {/*- end highlight -*/}
      <ComboBoxItem>Aardvark</ComboBoxItem>
      <ComboBoxItem>Cat</ComboBoxItem>
      <ComboBoxItem>Dog</ComboBoxItem>
      <ComboBoxItem>Kangaroo</ComboBoxItem>
      <ComboBoxItem>Panda</ComboBoxItem>
      <ComboBoxItem>Snake</ComboBoxItem>
    </ComboBox>
  );
}
```

## Forms

Use the `name` prop to submit the `id` of the selected item to the server. Set the `isRequired` prop to validate that the user selects a value, or implement custom client or server-side validation. See the [Forms](forms) guide to learn more.

```tsx render
"use client";
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';
import {Button} from 'vanilla-starter/Button';
import {Form} from 'vanilla-starter/Form';;

<Form>
  <ComboBox
    label="Animal"
    placeholder="e.g. Cat"
    /*- begin highlight -*/
    name="animal"
    isRequired
    /*- end highlight -*/
    description="Please select an animal.">
    <ComboBoxItem id="aardvark">Aardvark</ComboBoxItem>
    <ComboBoxItem id="cat">Cat</ComboBoxItem>
    <ComboBoxItem id="dog">Dog</ComboBoxItem>
    <ComboBoxItem id="kangaroo">Kangaroo</ComboBoxItem>
    <ComboBoxItem id="panda">Panda</ComboBoxItem>
    <ComboBoxItem id="snake">Snake</ComboBoxItem>
  </ComboBox>
  <Button type="submit">Submit</Button>
</Form>
```

## Popover

Use the `menuTrigger` prop to control when the popover opens:

* `input` (default): popover opens when the user edits the input text.
* `focus`: popover opens when the user focuses the input.
* `manual`: popover only opens when the user presses the trigger button or uses the arrow keys.

Use `allowsEmptyCollection` to keep the popover open when there are no items available in the list.

```tsx render wide docs={vanillaDocs.exports.ComboBox} links={vanillaDocs.links} props={['menuTrigger', 'allowsEmptyCollection']} initialProps={{label: 'Favorite Animal', placeholder: 'Select an animal'}}
"use client";
import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox';

<ComboBox/* PROPS */>
  <ComboBoxItem id="red panda">Red Panda</ComboBoxItem>
  <ComboBoxItem id="cat">Cat</ComboBoxItem>
  <ComboBoxItem id="dog">Dog</ComboBoxItem>
  <ComboBoxItem id="aardvark">Aardvark</ComboBoxItem>
  <ComboBoxItem id="kangaroo">Kangaroo</ComboBoxItem>
  <ComboBoxItem id="snake">Snake</ComboBoxItem>
</ComboBox>
```

## API

<Anatomy />

```tsx links={{ComboBox: '#combobox', Button: 'Button', Popover: 'Popover', ListBox: 'ListBox'}}
<ComboBox>
  <Label />
  <Input />
  <Button />
  <Text slot="description" />
  <FieldError />
  <Popover>
    <ListBox />
  </Popover>
</ComboBox>
```

### ComboBox

<PropTable component={docs.exports.ComboBox} links={docs.links} />
